﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.FaceSdk.Alignment;
using Microsoft.FaceSdk.Common;
using Microsoft.FaceSdk.Common.Extension;
using System.Windows.Media.Imaging;
using System.ComponentModel;
using Microsoft.FaceSdk.Detection;
using Microsoft.FaceSdk.Image;
using Microsoft.FaceSdk.ImageHelper;
using Microsoft.FaceSdk.Cartoon;
using Microsoft.FaceSdk.Cartoon.Framework;
using Microsoft.FaceSdk.Cartoon.Interfaces;

using SdkRect = Microsoft.FaceSdk.Common.Rectangle;
using Microsoft.Phone.Tasks;

namespace TrollFacing
{
    public partial class SketchYourFacePage : PhoneApplicationPage
    {
        private BitmapImage _originalImage;
        private PhotoChooserTask _photoChooserTask;
        private ICtnSketch _sketch;

        public SketchYourFacePage()
        {
            InitializeComponent();

            _photoChooserTask = new PhotoChooserTask();
            _photoChooserTask.Completed += PhotoChooserTaskCompleted;

            _originalImage = new BitmapImage(new Uri("/TrollFacing;component/Resources/Images/Demo/004.jpg", UriKind.Relative));
            Picture.Source = _originalImage;
        }

        private void SketchUp_Click(object sender, RoutedEventArgs e)
        {
            // Conversion au format image du SDK
            var sdkImg = ImageConverter.SystemToSdk(new WriteableBitmap(_originalImage));

            #region Initialisation du moteur de cartoon

            // Moteur de sketch
            CtnEngine engine = CtnEngine.Create();

            Guid generatorId = CtnGuid.GUID_GENERATOR_CAUCASIAN_MALE_VECTOR;

            //!\ Le générateur utilise WriteableBitmap, un UIElement, il est donc nécessaire
            //!\ d'appeller cette méthode depuis le thread de l'UI !
            ISketchGeneration generator = engine.GetSketchGenerator(generatorId);

            bool isExagerated = ExagerationCheckBox.IsChecked.GetValueOrDefault();
            bool isStyled = StyleCheckBox.IsChecked.GetValueOrDefault();

            #endregion

            System.Threading.ThreadPool.QueueUserWorkItem((System.Threading.WaitCallback)((o) =>
            {
                Random rand = new Random(DateTime.Now.Millisecond);

                // Taille du dessin
                int imageWidth = 512;
                int imageHeight = 512;

                // Création d'un sketch
                ICtnSketch sketch = CtnEngine.CreateSketch();
                sketch.Image = sdkImg;

                // Création des filtres
                IFaceDetection detector = engine.GetFaceDetector();
                IFaceAlignment alignmentor = engine.GetFaceAlignmentor();

                Dispatcher.BeginInvoke(() => { this.Infos.Text = "Détection du visage ..."; });

                // Détection du visage + recadrage
                var rect = detector.Detect(sketch);
                detector.FinalizeRect(sketch, rect);

                // Localise les points du visage
                alignmentor.LoadRawSketch(sketch);
                sketch = alignmentor.GetSketch();

                Dispatcher.BeginInvoke(() => { this.Infos.Text = "Génération du dessin ..."; });
                SdkRect rcBound = new SdkRect(0, imageWidth, 0, imageHeight);
                generator.Generate(sketch, rcBound);

                // On force le trait !
                if (isExagerated)
                {
                    Dispatcher.BeginInvoke(() => { this.Infos.Text = "On va exagérer un peu ..."; });
                    IFaceExaggeration exaggerator = engine.GetFaceExaggerator();
                    exaggerator.Exaggerate(sketch, ExaggerationType.AutoAddConstr, 0.0f);
                }

                // Impose ton style !
                if (isStyled)
                {
                    Dispatcher.BeginInvoke(() => { this.Infos.Text = "Applying templates ..."; });
                    var templateFilters = engine.GetCartoonTemplates(generatorId);
                    foreach (var tf in templateFilters)
                    {
                        if (tf.Count == 0) continue;

                        // Random sur certains templates
                        if (tf.TemplateId == CtnGuid.GUID_TEMPLATETYPE_GLASSES ||
                            tf.TemplateId == CtnGuid.GUID_TEMPLATETYPE_GLASS_P ||
                            tf.TemplateId == CtnGuid.GUID_TEMPLATETYPE_HAIR ||
                            tf.TemplateId == CtnGuid.GUID_TEMPLATETYPE_HAIR_P)
                        {
                            int index = rand.Next(0, tf.Count);
                            tf.Apply(sketch, index);
                        }
                    }
                }

                // Rendu
                Dispatcher.BeginInvoke(() => { this.Infos.Text = "Rendu ..."; });
                float ratio, tx, ty;
                sketch.FitToRect(SdkRect.FromLTWH(0, 0, imageWidth, imageHeight), out ratio, out tx, out ty);
                var sketchImage = sketch.Render(imageWidth, imageHeight);

                Dispatcher.BeginInvoke(() =>
                {
                    this.Infos.Text = string.Empty;
                    this.Picture.Source = ImageConverter.SdkToSystem(sketchImage);
                    this.ResetButton.Visibility = System.Windows.Visibility.Visible;
                    this.SketchUpButton.Visibility = System.Windows.Visibility.Collapsed;
                });
            }));
        }  

        private void Reset_Click(object sender, RoutedEventArgs e)
        {
            this.ResetButton.Visibility = System.Windows.Visibility.Collapsed;
            this.SketchUpButton.Visibility = System.Windows.Visibility.Visible;

            Picture.Source = _originalImage;
        }

        private void ChangePhoto_Click(object sender, RoutedEventArgs e)
        {
            _photoChooserTask.Show();
        }

        private void PhotoChooserTaskCompleted(object sender, PhotoResult e)
        {
            if (e.TaskResult == TaskResult.OK)
            {
                _originalImage = new BitmapImage();
                _originalImage.SetSource(e.ChosenPhoto);

                Picture.Source = _originalImage;
            }
        }
    }
}